Piano notes book, powered by Astro and React.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

119 line
3.0 KiB

  1. import createVerovioModule from 'verovio/wasm';
  2. import { VerovioToolkit } from 'verovio/esm';
  3. import { readFile, readdir } from 'node:fs/promises';
  4. import { JSDOM } from 'jsdom';
  5. import type {APIRoute, GetStaticPaths} from 'astro';
  6. const filter = (musicXml: string) => {
  7. const jsdom = new JSDOM(musicXml, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
  8. const win = jsdom.window;
  9. // const win = jsdom.window;
  10. return win.document.documentElement.outerHTML;
  11. };
  12. const processOutput = (xmlData: string) => {
  13. const jsdom = new JSDOM(xmlData, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
  14. const win = jsdom.window;
  15. const [svgElemsRoot, svgElemsMain] = Array.from(win.document.getElementsByTagName('svg'));
  16. if (typeof svgElemsRoot === 'undefined') {
  17. return '';
  18. }
  19. if (typeof svgElemsMain === 'undefined') {
  20. return '';
  21. }
  22. // svgElemsRoot.getAttributeNames().forEach((a) => {
  23. // const attr = svgElemsRoot.getAttribute(a);
  24. // if (!attr) {
  25. // return;
  26. // }
  27. // svgElemsMain.setAttribute(a, attr);
  28. // });
  29. Array.from(svgElemsRoot.children).forEach((h) => {
  30. if (h !== svgElemsMain) {
  31. h.remove();
  32. if (h.tagName.toLowerCase() === 'desc') {
  33. return;
  34. }
  35. if (svgElemsMain.children[0]) {
  36. svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
  37. return;
  38. }
  39. svgElemsMain.appendChild(h);
  40. }
  41. });
  42. Array.from(win.document.getElementsByClassName('pgHead')).forEach(h => {
  43. h.remove();
  44. });
  45. Array.from(win.document.getElementsByClassName('pgFoot')).forEach(h => {
  46. h.remove();
  47. });
  48. Array.from(win.document.getElementsByTagName('defs')).forEach(h => {
  49. h.remove();
  50. if (svgElemsMain) {
  51. if (svgElemsMain.children[0]) {
  52. svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
  53. } else {
  54. svgElemsMain.appendChild(h);
  55. }
  56. }
  57. });
  58. return `<?xml version="1.0" encoding="utf-8"?>${svgElemsMain.outerHTML}`
  59. };
  60. export const GET: APIRoute = async ({ params }) => {
  61. console.log('asdfasdf');
  62. const verovioModule = await createVerovioModule();
  63. const score = await readFile(`public/scores/${params.asset}.musicxml`, 'utf-8');
  64. const verovioToolkit = new VerovioToolkit(verovioModule);
  65. const filteredScore = filter(score);
  66. const isSuccessful = verovioToolkit.loadData(filteredScore);
  67. if (!isSuccessful) {
  68. return new Response(null, { status: 500 });
  69. }
  70. verovioToolkit.setOptions({
  71. breaks: 'none',
  72. font: 'Bravura',
  73. });
  74. let data: string;
  75. try {
  76. const raw = verovioToolkit.renderToSVG(1)
  77. .replace(/xmlns:mei="(.+?)"/g, '')
  78. .replace(/xlink:/g, '');
  79. data = processOutput(raw);
  80. } catch (err) {
  81. console.error(err);
  82. return new Response(null, { status: 500 });
  83. }
  84. return new Response(
  85. data,
  86. {
  87. headers: {
  88. 'Content-Type': 'image/svg+xml',
  89. },
  90. status: 200,
  91. }
  92. );
  93. };
  94. export const getStaticPaths: GetStaticPaths = async () => {
  95. const files = await readdir('public/scores');
  96. return files
  97. .filter((f) => f.endsWith('.musicxml'))
  98. .map((f) => ({
  99. params: {
  100. asset: f.replace(/\.musicxml/g, ''),
  101. },
  102. }));
  103. };